package com.nofeng.wx.utils;

import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

public class IndicatrixUtils {

    public static BigDecimal avg(List<BigDecimal> list){
        if(CollectionUtils.isEmpty(list)){
            return new BigDecimal("0.000");
        }
        BigDecimal sum=BigDecimal.valueOf(0);
        for (BigDecimal bigDecimal : list) {
            sum=sum.add(bigDecimal);
        }
        return sum.divide(BigDecimal.valueOf(list.size()),2,RoundingMode.DOWN);
    }

    public static BigDecimal sum(List<BigDecimal> list){
        BigDecimal sum=BigDecimal.valueOf(0);
        for (BigDecimal bigDecimal : list) {
            sum=sum.add(bigDecimal);
        }
        return sum;
    }

    public static BigDecimal min(List<BigDecimal> list){
        return  list.stream().min(BigDecimal::compareTo).get();
    }

    public static BigDecimal max(List<BigDecimal> list){
        return  list.stream().max(BigDecimal::compareTo).get();
    }
    public static BigDecimal max(List<BigDecimal> list,BigDecimal defaultValue){
        if(CollectionUtils.isEmpty(list)){
            return defaultValue;
        }
        return  list.stream().max(BigDecimal::compareTo).get();
    }



    public static double[] ma(int dayCount,double[] closePrice){
        double[] ma=new double[closePrice.length];
        for (int days  = 0; days  <closePrice.length ; days ++) {
            BigDecimal sum=BigDecimal.valueOf(0);
            if(days<dayCount-1){
                ma[days]=sum.doubleValue();
                continue;
            }
            for (int dataIdx  = 0; dataIdx  < dayCount; dataIdx ++) {
                sum=sum.add(BigDecimal.valueOf(closePrice[days-dataIdx]));
            }
            ma[days]=sum.divide(BigDecimal.valueOf(dayCount),2, RoundingMode.DOWN).doubleValue();
        }
        return ma;

    }

    public static void macd(double[] closePrice, int fast, int slow, int signal, double[] macd, double[] dea, double[] diff) {
        double preEma_12 = 0;
        double preEma_26 = 0;
        double preDEA = 0;

        double ema_12 = 0;
        double ema_26 = 0;

        double fastPeriod = Double.valueOf(new Integer(fast));// 日快线移动平均，标准为12，按照标准即可
        double slowPeriod = Double.valueOf(new Integer(slow));// 日慢线移动平均，标准为26，可理解为天数
        double signalPeriod = Double.valueOf(new Integer(signal));// 日移动平均，标准为9，按照标准即可

        double DEA = 0;
        double DIFF = 0;
        double MACD = 0;
        for (int i = 0; i < closePrice.length; i++) {
            ema_12 = i == 0 ? closePrice[i]
                    : MathCaclateUtil.add(
                    MathCaclateUtil.divide(
                            MathCaclateUtil.multiply(preEma_12, fastPeriod - 1, BigDecimal.ROUND_HALF_UP),
                            fastPeriod + 1, BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(
                            MathCaclateUtil.multiply(closePrice[i], 2D, BigDecimal.ROUND_HALF_UP),
                            fastPeriod + 1, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// ema_12=preEma_12*(fastPeriod-1)/(fastPeriod+1)+closePrice*2/(fastPeriod+1)

            ema_26 = i == 0 ? closePrice[i]
                    : MathCaclateUtil.add(
                    MathCaclateUtil.divide(
                            MathCaclateUtil.multiply(preEma_26, slowPeriod - 1, BigDecimal.ROUND_HALF_UP),
                            slowPeriod + 1, BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(
                            MathCaclateUtil.multiply(closePrice[i], 2D, BigDecimal.ROUND_HALF_UP),
                            slowPeriod + 1, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// ema_26=preEma_26*(slowPeriod-1)/(slowPeriod+1)+closePrice*2/(slowPeriod+1)

            DIFF = i == 0 ? 0 : MathCaclateUtil.subtract(ema_12, ema_26, BigDecimal.ROUND_HALF_UP);// Diff=ema_12-ema_26

            DEA = i == 0 ? 0
                    : MathCaclateUtil.add(
                    MathCaclateUtil.divide(
                            MathCaclateUtil.multiply(preDEA, signalPeriod - 1, BigDecimal.ROUND_HALF_UP),
                            signalPeriod + 1, BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(DIFF, 2D, BigDecimal.ROUND_HALF_UP),
                            signalPeriod + 1, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// DEA=preDEA*(signalPeriod-1)/(signalPeriod+1)+Diff*2/(signalPeriod+1)

            MACD = i == 0 ? 0
                    : MathCaclateUtil.multiply(2D, MathCaclateUtil.subtract(DIFF, DEA, BigDecimal.ROUND_HALF_UP),
                    BigDecimal.ROUND_HALF_UP);// MACD=2×(Diff－DEA)

            preEma_12 = ema_12;
            preEma_26 = ema_26;
            preDEA = DEA;

            macd[i] = MACD;
            dea[i] = DEA;
            diff[i] = DIFF;
        }
    }

    /**
     * 计算股价的上下平均浮动（标准差）
     *
     * @param fluctuations 股价浮动百分比列表
     * @return 标准差
     */
    public static double calculateFluctuation(List<Double> fluctuations) {
        if (fluctuations == null || fluctuations.isEmpty()) {
            throw new IllegalArgumentException("股价浮动百分比数据不能为空");
        }

        // 计算平均浮动百分比
        double mean = calculateMean(fluctuations);

        // 计算每个浮动百分比与平均值的差的平方和
        double sumOfSquares = fluctuations.stream()
                .map(fluctuation -> (fluctuation - mean) * (fluctuation - mean))
                .reduce(0.0, Double::sum);

        // 计算方差
        double variance = sumOfSquares / (fluctuations.size() - 1);

        // 计算标准差
        return Math.sqrt(variance);
    }

    /**
     * 计算平均浮动百分比
     *
     * @param fluctuations 股价浮动百分比列表
     * @return 平均浮动百分比
     */
    private static double calculateMean(List<Double> fluctuations) {
        return fluctuations.stream().mapToDouble(Double::doubleValue).average().orElse(0.0);
    }

    public void kdj(double[] maxPrice, double[] minPrice, double[] closePrice, int fastK, int slowK, int slowD,
                    double[] K_R, double[] D_R, double[] J_R) {
        List<Double> highestPriceList = new ArrayList<>();
        List<Double> lowestPriceList = new ArrayList<>();

        List<Double> highestPriceList_temp = new ArrayList<>();
        List<Double> lowestPriceList_temp = new ArrayList<>();

        double RSV = 0;

        double fastK_Period = fastK;// 标准值为9
        double slowK_Period = Double.valueOf(new Integer(slowK));// 标准值为3
        double slowD_Period = Double.valueOf(new Integer(slowD));// 标准值为3

        double preK = 0;
        double preD = 0;

        double K = 0;
        double D = 0;
        double J = 0;
        for (int i = 0; i < closePrice.length; i++) {
            if (highestPriceList.size() == fastK_Period) {
                highestPriceList.remove(0);
                lowestPriceList.remove(0);
            } else if (highestPriceList.size() == 0) {
                highestPriceList.add(maxPrice[i]);
                lowestPriceList.add(minPrice[i]);
            }
            highestPriceList.add(maxPrice[i]);
            lowestPriceList.add(minPrice[i]);

            highestPriceList_temp = new ArrayList<>();
            lowestPriceList_temp = new ArrayList<>();

            highestPriceList_temp.addAll(highestPriceList);
            lowestPriceList_temp.addAll(lowestPriceList);

//            highestPriceList_temp = MathCaclateUtil.sortList(highestPriceList_temp);
//            lowestPriceList_temp = MathCaclateUtil.sortList(lowestPriceList_temp);

            RSV = MathCaclateUtil.multiply(MathCaclateUtil.divide(
                    MathCaclateUtil.subtract(closePrice[i], lowestPriceList_temp.get(0), BigDecimal.ROUND_HALF_UP),
                    MathCaclateUtil.subtract(highestPriceList_temp.get(highestPriceList_temp.size() - 1),
                            lowestPriceList_temp.get(0), BigDecimal.ROUND_HALF_UP),
                    BigDecimal.ROUND_UNNECESSARY), 100D, BigDecimal.ROUND_HALF_UP);// （今日收盘价－9日内最低价）÷（9日内最高价－9日内最低价）×100

            // 如果无前一日的K、D值
            K = MathCaclateUtil.divide(MathCaclateUtil.add(RSV,
                    MathCaclateUtil.multiply(2D, preK, BigDecimal.ROUND_HALF_UP), BigDecimal.ROUND_HALF_UP),
                    slowK_Period, BigDecimal.ROUND_UNNECESSARY);// （当日RSV值+2×前一日K值）÷3

            D = MathCaclateUtil.divide(MathCaclateUtil.add(K,
                    MathCaclateUtil.multiply(2D, preD, BigDecimal.ROUND_HALF_UP), BigDecimal.ROUND_HALF_UP),
                    slowD_Period, BigDecimal.ROUND_UNNECESSARY);// （当日K值+2×前一日D值）÷3

            J = MathCaclateUtil.subtract(MathCaclateUtil.multiply(3D, K, BigDecimal.ROUND_HALF_UP),
                    MathCaclateUtil.multiply(2D, D, BigDecimal.ROUND_HALF_UP), BigDecimal.ROUND_HALF_UP);// 3K－2D

            preK = K;
            preD = D;

            K_R[i] = K;
            D_R[i] = D;
            J_R[i] = J;
        }
    }

    public static void rsi(double[] closePrice, int rsi1_n, int rsi2_n, int rsi3_n, double[] rsi1, double[] rsi2,
                    double[] rsi3) {
        double pp_6;// 用于计算rsi数据
        double np_6;
        double pp_12;
        double np_12;
        double pp_24;
        double np_24;
        double prepp_6 = 0;// 用于计算rsi数据
        double prenp_6 = 0;
        double prepp_12 = 0;
        double prenp_12 = 0;
        double prepp_24 = 0;
        double prenp_24 = 0;

        double upsanddowns;
        double n1 = Double.valueOf(new Integer(rsi1_n));// 标准值为6
        double n2 = Double.valueOf(new Integer(rsi2_n));// 标准值为12
        double n3 = Double.valueOf(new Integer(rsi3_n));// 标准值为24
        double num_100 = 100D;

        double RSI1 = 0;
        double RSI2 = 0;
        double RSI3 = 0;
        for (int i = 0; i < closePrice.length; i++) {
            if (i == 0) {
                continue;
            }
            upsanddowns = closePrice[i] - closePrice[i - 1];

            pp_6 = MathCaclateUtil.add(
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(prepp_6, n1 - 1, BigDecimal.ROUND_HALF_UP), n1,
                            BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(upsanddowns >= 0 ? upsanddowns : 0, n1, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// prepp_6*(6-1)/6+(upsanddowns>=0?upsanddowns:0)/6
            np_6 = MathCaclateUtil.add(
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(prenp_6, n1 - 1, BigDecimal.ROUND_HALF_UP), n1,
                            BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(upsanddowns >= 0 ? 0 : upsanddowns, n1, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// prenp_6*(6-1)/6+(upsanddowns>=0?0:upsanddowns)/6
            RSI1 = MathCaclateUtil.divide(MathCaclateUtil.multiply(num_100, pp_6, BigDecimal.ROUND_HALF_UP),
                    MathCaclateUtil.add(pp_6, -np_6, BigDecimal.ROUND_HALF_UP), BigDecimal.ROUND_UNNECESSARY);// 100*pp_6/(pp_6-np_6)

            rsi1[i] = RSI1;
            prepp_6 = pp_6;
            prenp_6 = np_6;

            pp_12 = MathCaclateUtil.add(
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(prepp_12, n2 - 1, BigDecimal.ROUND_HALF_UP), n2,
                            BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(upsanddowns >= 0 ? upsanddowns : 0, n2, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// prepp_12*(12-1)/12+(upsanddowns>=0?upsanddowns:0)/12;
            np_12 = MathCaclateUtil.add(
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(prenp_12, n2 - 1, BigDecimal.ROUND_HALF_UP), n2,
                            BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(upsanddowns >= 0 ? 0 : upsanddowns, n2, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// prenp_12*(12-1)/12+(upsanddowns>=0?0:upsanddowns)/12;
            RSI2 = MathCaclateUtil.divide(MathCaclateUtil.multiply(num_100, pp_12, BigDecimal.ROUND_HALF_UP),
                    MathCaclateUtil.add(pp_12, -np_12, BigDecimal.ROUND_HALF_UP), BigDecimal.ROUND_UNNECESSARY);// 100*pp_12/(pp_12-np_12);

            rsi2[i] = RSI2;
            prepp_12 = pp_12;
            prenp_12 = np_12;

            pp_24 = MathCaclateUtil.add(
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(prepp_24, n3 - 1, BigDecimal.ROUND_HALF_UP), n3,
                            BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(upsanddowns >= 0 ? upsanddowns : 0, n3, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// prepp_24*(24-1)/24+(upsanddowns>=0?upsanddowns:0)/24;
            np_24 = MathCaclateUtil.add(
                    MathCaclateUtil.divide(MathCaclateUtil.multiply(prenp_24, n3 - 1, BigDecimal.ROUND_HALF_UP), n3,
                            BigDecimal.ROUND_UNNECESSARY),
                    MathCaclateUtil.divide(upsanddowns >= 0 ? 0 : upsanddowns, n3, BigDecimal.ROUND_UNNECESSARY),
                    BigDecimal.ROUND_HALF_UP);// prenp_24*(24-1)/24+(upsanddowns>=0?0:upsanddowns)/24;
            RSI3 = MathCaclateUtil.divide(MathCaclateUtil.multiply(num_100, pp_24, BigDecimal.ROUND_HALF_UP),
                    MathCaclateUtil.add(pp_24, -np_24, BigDecimal.ROUND_HALF_UP), BigDecimal.ROUND_UNNECESSARY);// 100*pp_24/(pp_24-np_24);

            rsi3[i] = RSI3;
            prepp_24 = pp_24;
            prenp_24 = np_24;
        }
    }


    public static double getPercentileWithBigDecimal(List<BigDecimal> dataList, double p) {
        if(CollectionUtils.isEmpty(dataList)){
            return 0.00;
        }
        List<Double> doubleDataList= CollectionUtils.emptyIfNull(dataList).stream().map(l->l.doubleValue()).collect(Collectors.toList());
        return getPercentile(doubleDataList, p);

    }

    public static double getPercentileWithBigDecimal2(List<BigDecimal> dataList, double p) {
        if(CollectionUtils.isEmpty(dataList)){
            return 0.00;
        }
        List<Double> doubleDataList= CollectionUtils.emptyIfNull(dataList).stream().map(l->l.doubleValue()).collect(Collectors.toList());
        return getPercentile2(doubleDataList, p);

    }

    /**
     * 分位值
     * @param dataList
     * @param p
     * @return
     */
    public static double getPercentile(List<Double> dataList, double p) {
        int n = dataList.size();
        dataList.sort(new Comparator<Double>() {
            //从小到大排序
            @Override
            public int compare(Double o1, Double o2) {
                if(o1 == null || o2== null){
                    return 0;
                }
                return o1.compareTo(o2);
            }
        });
        double px =  p*(n-1);
        int i = (int) Math.floor(px);
        double g = px - i;
        if(g==0){
            return dataList.get(i);
        }else{
            return (1-g)*dataList.get(i)+g*dataList.get(i+1);
        }
    }

    public static List<Double> toDouble(List<BigDecimal> list){
        List<Double> result= Lists.newArrayList();
        for (BigDecimal bigDecimal : CollectionUtils.emptyIfNull(list)) {
            result.add(bigDecimal.doubleValue());
        }
        return  result;
    }

    public static double getPercentile2(List<Double> dataList, double p) {
        int n = dataList.size();
        dataList.sort(new Comparator<Double>() {
            //从小到大排序
            @Override
            public int compare(Double o1, Double o2) {
                if(o1 == null || o2== null){
                    return 0;
                }
                return o1.compareTo(o2);
            }
        });

        int i = (int) Math.floor(n*p);

        return dataList.get(i);
    }
    public static double[] findMostOverlapPodoubles(double[][] data) {
        Map<Double, Double> highCounts = new HashMap<>();
        Map<Double, Double> lowCounts = new HashMap<>();
        // 遍历所有数组
        for (double[] podoubles : data) {
            // 更新高点和低点的计数
            double high=podoubles[0];
            double low=podoubles[1];
            if(podoubles[1]>podoubles[0]){
                high=podoubles[1];
                low=podoubles[0];
            }
            highCounts.put(high, highCounts.getOrDefault(high, 0.00) + 1);
            lowCounts.put(low, lowCounts.getOrDefault(low, 0.00) + 1);
        }
        // 找出出现次数最多的高点和低点
        double mostOverlapHigh = findMaxKey(highCounts);
        double mostOverlapLow = findMaxKey(lowCounts);
        return new double[]{mostOverlapHigh, mostOverlapLow};
    }
    private static double findMaxKey(Map<Double, Double> counts) {
        double maxKey = -1;
        double maxCount = 0;
        for (Map.Entry<Double, Double> entry : counts.entrySet()) {
            if (entry.getValue() > maxCount) {
                maxCount = entry.getValue();
                maxKey = entry.getKey();
            }
        }
        return maxKey;
    }


}
